import Singleton from "./Singleton";

interface eveItem {
  func: Function;
  ctx: unknown;
}

class EventEmitter extends Singleton {
  static get Instance() {
    return super.getInstance<EventEmitter>();
  }
  private eventDic: Map<string, Array<eveItem>> = new Map();
  on(eventName: string, func: Function, ctx?: unknown) {
    if (this.eventDic.has(eventName)) {
      this.eventDic.get(eventName)?.push({ func, ctx });
    } else {
      this.eventDic.set(eventName, [{ func, ctx }]);
    }
  }
  off(eventName: string, func: Function) {
    let callbacks = this.eventDic.get(eventName) as Array<eveItem>;
    if (callbacks) {
      callbacks = callbacks.filter((obj) => obj.func !== func);
    }
  }
  emit(eventName: string, ...args: unknown[]) {
    let callbacks = this.eventDic.get(eventName) as Array<eveItem>;
    if (callbacks) {
      callbacks.forEach(({ ctx, func }) => {
        ctx ? func.apply(ctx, args) : func(args);
      });
    }
  }
  once(eventName: string, func: Function, ctx?: unknown) {
    let _this: EventEmitter = this;
    _this.on(
      eventName,
      (...args: unknown[]) => {
        func(args);
        _this.off(eventName, func);
      },
      ctx
    );
  }
  clear() {
    this.eventDic.clear();
  }
}

export const EventEmitterIns = EventEmitter.Instance;
